﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;

namespace DocFactoryLibrary
{
    public class TemplateParser
    {
        Dictionary<string, string> Values = new Dictionary<string, string>();

        #region Constructors 
        public virtual void TemplateToRandomDocument(string TemplateFilePath, string SaveDirectoryPath, out string FileName)
        {
            FileName = ProcessXMLDocument(TemplateFilePath, ToSafeDirectoryPath(SaveDirectoryPath), "");
        }

        public virtual void TemplateToDocument(string TemplateFilePath, string SaveDirectoryPath, string FileName)
        {
            //TODO: Make sure that the file does not already exist.

            if (FileName != "")
                ProcessXMLDocument(TemplateFilePath, SaveDirectoryPath, FileName);
            else
                throw new Exception("You must provide a valid filename.");

        }
        #endregion

        #region Data Processing
        private string MakeFullTag(XmlNodeList nodes, int StartTag)
        {

            string strCompleteTag = "";

            bool IsInRange = (StartTag - 1 >= 0) && (nodes.Count >= StartTag + 1);

            if (IsInRange)
            {

                bool IsTag = (nodes[StartTag - 1].InnerText == "<<") && (nodes[StartTag + 1].InnerText == ">>");

                if (IsTag)
                {

                    strCompleteTag = string.Format("{0}{1}{2}",

                        nodes[StartTag - 1].InnerText, nodes[StartTag].InnerText, nodes[StartTag + 1].InnerText);

                }

            }

            return strCompleteTag;

        }
        private void UpdateNode(XmlNode node, int NodeIndex, string InnerText, XmlDocument doc)
        {

            if (InnerText.Contains("\n"))
            {

                node.InnerText = "";

                string[] strLines = InnerText.Split('\n');
                XmlNode xmlPreviousNode = null;

                XmlNode xmlAss = null;

                foreach (string strLine in strLines)
                {
                    if ( (strLine != "") && (strLine != "\n") )
                    {
                        if (xmlPreviousNode == null)
                            xmlPreviousNode = node;

                        if (xmlAss == null)
                        {
                            xmlAss = node;

                            do
                            {
                                if (node.ParentNode != null)
                                    xmlAss = xmlAss.ParentNode;
                                else
                                {
                                    xmlAss = null;
                                    break;
                                }
                            } while ((xmlAss.Name == "w:t") || (xmlAss.Name == "w:r") );
                        }


                        XmlElement xmlNewParagraph = doc.CreateElement("w:p", "http://schemas.microsoft.com/office/word/2003/wordml");
                        xmlNewParagraph.Prefix = "w";

                        XmlElement xmlNewRR = doc.CreateElement("w:r", "http://schemas.microsoft.com/office/word/2003/wordml");
                        xmlNewParagraph.Prefix = "w";

                        XmlElement xmlNewElement = doc.CreateElement("w:t", "http://schemas.microsoft.com/office/word/2003/wordml");
                        xmlNewElement.Prefix = "w";



                        xmlNewParagraph.AppendChild(xmlNewRR);
                        xmlNewRR.AppendChild(xmlNewElement);

                        xmlNewElement.InnerText = strLine;

                        if (xmlAss != null)
                            xmlAss.AppendChild(xmlNewParagraph);
                        else
                            node.AppendChild(xmlNewParagraph);

                        xmlPreviousNode = xmlNewParagraph;
                    }
                }
               


            }

            else
            {

                node.InnerText = InnerText;

            }

        }

        private string ProcessXMLDocument(string TemplateFilePath, string SaveDirectoryPath, string FileName )
        {
            // Create a new XmlDocument   
            XmlDocument doc = new XmlDocument();

            // Load data   
            doc.Load(TemplateFilePath);

            ////// Set up namespace manager for XPath   
            XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
            ns.AddNamespace("w", "http://schemas.microsoft.com/office/word/2003/wordml");

            // Get Literal Text with XPath   
            XmlNodeList nodes = doc.SelectNodes("//w:t", ns);

            // An Opennig Tag Was Found
            bool StartTag = false;

            int NodeIndex = 0;

            

            foreach (XmlNode node in nodes)
            {
                if (node.InnerText == "<<")
                {
                    string strFullText = MakeFullTag(nodes, NodeIndex + 1);

                    if (strFullText != "")
                    {
                        UpdateNode(nodes[NodeIndex + 1], NodeIndex + 1, InnerMatches(strFullText), doc);
                        nodes[NodeIndex].InnerText = "";
                        nodes[NodeIndex + 2].InnerText = "";
                    }

                }

                else
                {
                    UpdateNode(nodes[NodeIndex], NodeIndex, InnerMatches(node.InnerText), doc); 
                }

                NodeIndex++;


            }

            if (FileName == "")
                FileName = DateTime.Now.ToFileTime().ToString() ;

            XmlTextWriter objXmlTxtWritter = new XmlTextWriter(SaveDirectoryPath + FileName + ".xml ", null);

            doc.WriteTo(objXmlTxtWritter);
            objXmlTxtWritter.Flush();
            objXmlTxtWritter.Close();

            return FileName + ".xml";
        }
        public virtual void MergeData(Dictionary<string, string> objInput)
        {
            Values = objInput;
        }


        public virtual void PopulateTags(System.Data.SqlClient.SqlDataReader ValuesData)
        {

            while (ValuesData.Read())
            {
                for (int i = 0; i < ValuesData.FieldCount; i++)
                {
                    object oInput = ValuesData[i];

                    if (oInput == null)
                        oInput = "";

                    if (oInput is DateTime)
                    {
                        DateTime oInputDate = (DateTime)oInput;
                        Values.Add(string.Format("<<{0}>>", ValuesData.GetName(i).ToUpper()), oInputDate.ToShortDateString());

                    }
                    else
                    {
                        Values.Add(string.Format("<<{0}>>", ValuesData.GetName(i).ToUpper()), oInput.ToString());
                    }
                }
            }

            if (ValuesData.NextResult())
                PopulateTags(ValuesData);
        }


        string InnerMatches(string Input)
        {
            MatchEvaluator MatchEval = new MatchEvaluator(HashTableReplace);
            Regex objNotNaturalPattern = new Regex("<<(.*?)>>");

            Input = objNotNaturalPattern.Replace(Input, MatchEval);

            return Input;
        }

        string HashTableReplace(Match m)
        {
            string ReturnString = "TagButNoMatch";

            if (Values.ContainsKey(m.Value.ToUpper()))
                ReturnString = (string)Values[m.Value.ToUpper()];

            return ReturnString;
        }
        #endregion

        #region IO Methods
         private string ToSafeDirectoryPath(string DirectoryPath)
        {
            string strReturnPath = "";

            if (!DirectoryPath.EndsWith(@"\"))
                strReturnPath = DirectoryPath + @"\";
            else
                strReturnPath = DirectoryPath; 

            return strReturnPath; 
        }
        #endregion
        
    }
}
